{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Part 3: リモートデータを操作するための高度ツール\n",
    "\n",
    "前回のセクションではFederated Learningを使ってトイモデル（初歩的なモデル）の学習を行いました。`.send()`や`.get()`を使う事でモデルをデータのある場所へ送ってパラメータをアップデートしたり、学習済みモデルを受け取ったりできることを確認しました。しかし、前回の最後になって、プライバシーを守るためにはそれだけでは不十分だということに気がつきました。もう少し正確に言うと、`.get()`する前に勾配ベクトルを平均する必要があるということです。`.get()`実行前にワーカー全員の勾配ベクトルの平均を取れれば、もう個別のワーカーの勾配ベクトルを知る事はできません。（結果としてプライバシーの保護レベルを高めることができます。）\n",
    "\n",
    "しかし、このテクニックを実行するためには、次の事ができなくてはなりません。\n",
    "\n",
    "- ポインタtensorのAPIを使って、あるワーカーから別のワーカーへtensorを直接送れること\n",
    "\n",
    "本セクションでは、上記に加え、今後のチュートリアルで使用するいくつかの高度なオペレーションについても学びます。\n",
    "\n",
    "Authors:\n",
    "- Andrew Trask - Twitter: [@iamtrask](https://twitter.com/iamtrask)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import syft as sy\n",
    "hook = sy.TorchHook(torch)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Section 3.1 - ポインタのポインタ\n",
    "\n",
    "ここまでに学習した通り、PointerTensorは通常のtensorと同じような扱いが可能です。実際、PointerTensorとtensorは実に良く似ているので、そのままPointerTensorのポインタを作る事もできます。実際に確認してみましょう。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob = sy.VirtualWorker(hook, id='bob')\n",
    "alice = sy.VirtualWorker(hook, id='alice')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 通常のtensorです。ローカルに存在します。\n",
    "x = torch.tensor([1,2,3,4])\n",
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# tensorをBobへ送ります。戻り値としてPointerTensorを受け取ります。\n",
    "x_ptr = x.send(bob)\n",
    "\n",
    "# Bobが持っているtensorへのポインタです。\n",
    "x_ptr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# ではPointerTensorをAliceへ送ってみましょう。\n",
    "pointer_to_x_ptr = x_ptr.send(alice)\n",
    "\n",
    "pointer_to_x_ptr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 何がどうなった？\n",
    "\n",
    "直前の例では、私たちは`x`という名前でtensorを作成し、Bobへ送りました。私たちは`x_ptr`という名前でポインタを保持しています。 \n",
    "\n",
    "さらに、`x_ptr.send(alice)`を実行し、**ポインタ**をAliceへ送りました。\n",
    "\n",
    "注記: ポインタを第三者へ送ってもデータそのものは移動されません。データへのポインタだけが送られています。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 上記の通り、Bobがまだ実際のデータを保持しています。（実データは常にtensor型で保持されます）\n",
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 一方でAliceはx_ptrを保持しています。（x_ptrがBob保有のtensorを参照していることを確認してください）\n",
    "alice._objects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# そして、私たちは.get()を使う事でAliceからx_ptrを受け取る（返してもらう？）ことができます。\n",
    "x_ptr = pointer_to_x_ptr.get()\n",
    "x_ptr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# そして、x_ptrに対して.get()を実行することでBobからxを受け取る（返してもらう）ことができます。\n",
    "\n",
    "x = x_ptr.get()\n",
    "x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ポインタのポインタを使っての演算\n",
    "\n",
    "通常のポインタと同様、私たちはポインタのポインタに対してもPyTorchの演算処理を実行することが可能です。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "alice._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p2p2x = torch.tensor([1,2,3,4,5]).send(bob).send(alice)\n",
    "\n",
    "y = p2p2x + p2p2x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "alice._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "y.get().get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "alice._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p2p2x.get().get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "alice._objects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Section 3.2 - ポインタにおけるチェーンオペレーション\n",
    "\n",
    "ここまでの例では、`.send()`や`.get()`を使う事でローカルマシンからワーカーへtensorやポインタを送信したりワーカーからtensorやポインタを受信したりしてきました。しかし、ポインタのチェーンを扱うようになってくると、あるワーカーから別のワーカーへ直接tensorを送って欲しいことがあります。そうなってくると、そのために特別にデザインされた特別な関数が欲しくなりますよね。\n",
    "\n",
    "そんなオペレーションの一つは\n",
    "\n",
    "- `my_pointer2pointer.move(another_worker)`です。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# xはBobのマシンにあるtensorのポインタです。\n",
    "x = torch.tensor([1,2,3,4,5]).send(bob)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('  bob:', bob._objects)\n",
    "print('alice:',alice._objects)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = x.move(alice)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('  bob:', bob._objects)\n",
    "print('alice:',alice._objects)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "エクセレント！ これにより、信頼できるアグリゲーター（集計者）による**gradient averaging**（勾配ベクトルの平均化）の準備が整いました。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# おめでとうございます！コミュニティに入ろう！\n",
    "\n",
    "本チュートリアルを完了しました。おめでとうございます！もし、このチュートリアルを気に入って、プライバシーに配慮した非中央集権的なAI技術や付随する（データやモデルの）サプライチェーンにご興味があって、プロジェクトに参加したいと思われるなら、以下の方法で可能です。\n",
    "\n",
    "### PySyftのGitHubレポジトリにスターをつける\n",
    "\n",
    "一番簡単に貢献できる方法はこのGitHubのレポジトリにスターを付けていただくことです。スターが増えると露出が増え、より多くのデベロッパーにこのクールな技術の事を知って貰えます。\n",
    "\n",
    "- [Star PySyft](https://github.com/OpenMined/PySyft)\n",
    "\n",
    "### Slackに入る\n",
    "\n",
    "最新の開発状況のトラッキングする一番良い方法はSlackに入ることです。\n",
    "下記フォームから入る事ができます。\n",
    "[http://slack.openmined.org](http://slack.openmined.org)\n",
    "\n",
    "### コードプロジェクトに参加する\n",
    "\n",
    "コミュニティに貢献する一番良い方法はソースコードのコントリビューターになることです。PySyftのGitHubへアクセスしてIssueのページを開き、\"Projects\"で検索してみてください。参加し得るプロジェクトの状況を把握することができます。また、\"good first issue\"とマークされているIssueを探す事でミニプロジェクトを探すこともできます。\n",
    "\n",
    "- [PySyft Projects](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3AProject)\n",
    "- [Good First Issue Tickets](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n",
    "\n",
    "### 寄付\n",
    "\n",
    "もし、ソースコードで貢献できるほどの時間は取れないけど、是非何かサポートしたいという場合は、寄付をしていただくことも可能です。寄附金の全ては、ハッカソンやミートアップの開催といった、コミュニティ運営経費として利用されます。\n",
    "\n",
    "[OpenMined's Open Collective Page](https://opencollective.com/openmined)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
